{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "49e9c69e",
      "metadata": {
        "id": "49e9c69e"
      },
      "source": [
        "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/06-langchain-agents.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/06-langchain-agents.ipynb)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1571b632",
      "metadata": {
        "id": "1571b632"
      },
      "source": [
        "#### [LangChain Handbook](https://pinecone.io/learn/langchain)\n",
        "\n",
        "# Agents 🤖\n",
        "\n",
        "Agents are like \"tools\" for LLMs. They allow a LLM to access Google search, perform complex calculations with Python, and even make SQL queries.\n",
        "\n",
        "In this notebook we'll explore agents and how to use them in LangChain.\n",
        "\n",
        "We'll start by installing the prerequisite libraries that we'll be using in this example."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "uZR3iGJJtdDE",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "uZR3iGJJtdDE",
        "outputId": "0119ff4e-61fc-4d5f-9292-a719c54b8cb5"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.5/1.5 MB\u001b[0m \u001b[31m10.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.5/75.5 kB\u001b[0m \u001b[31m6.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h  Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
            "  Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
            "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.4/49.4 kB\u001b[0m \u001b[31m4.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h  Building wheel for google-search-results (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
            "  Building wheel for wikipedia (setup.py) ... \u001b[?25l\u001b[?25hdone\n"
          ]
        }
      ],
      "source": [
        "!pip install -qU langchain openai google-search-results wikipedia sqlalchemy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "wPdWz1IdxyBR",
      "metadata": {
        "id": "wPdWz1IdxyBR"
      },
      "source": [
        "To run this notebook, we will need to use an OpenAI LLM. Here we will setup the LLM we will use for the whole notebook, just input your openai api key when prompted."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "c02c4fa2",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "c02c4fa2",
        "outputId": "ad22dddc-6180-48ac-86dd-265b6b7afa57"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "··········\n"
          ]
        }
      ],
      "source": [
        "from getpass import getpass\n",
        "\n",
        "OPENAI_API_KEY = getpass()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "73bfcbb6",
      "metadata": {
        "id": "73bfcbb6"
      },
      "outputs": [],
      "source": [
        "from langchain import OpenAI\n",
        "\n",
        "llm = OpenAI(\n",
        "    openai_api_key=OPENAI_API_KEY,\n",
        "    temperature=0\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "309g_2pqxzzB",
      "metadata": {
        "id": "309g_2pqxzzB"
      },
      "source": [
        "As we did before, we will be counting our tokens in each call."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "DsC3szr6yP3L",
      "metadata": {
        "id": "DsC3szr6yP3L"
      },
      "outputs": [],
      "source": [
        "from langchain.callbacks import get_openai_callback\n",
        "\n",
        "def count_tokens(agent, query):\n",
        "    with get_openai_callback() as cb:\n",
        "        result = agent(query)\n",
        "        print(f'Spent a total of {cb.total_tokens} tokens')\n",
        "\n",
        "    return result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bd60a97c",
      "metadata": {
        "id": "bd60a97c"
      },
      "source": [
        "With all of that set up, let's jump into **Agents**."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6e1f31b4",
      "metadata": {
        "id": "6e1f31b4"
      },
      "source": [
        "## What is an agent?"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5b919c3a",
      "metadata": {
        "id": "5b919c3a"
      },
      "source": [
        "**Definition**: The key behind agents is giving LLM's the possibility of using tools in their workflow. This is where langchain departs from the popular chatgpt implementation and we can start to get a glimpse of what it offers us as builders. Until now, we covered several building blocks in isolation. Let's see them come to life.\n",
        "\n",
        "The official definition of agents is the following:\n",
        "\n",
        "\n",
        "> Agents use an LLM to determine which actions to take and in what order. An action can either be using a tool and observing its output, or returning to the user."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6c9c13e9",
      "metadata": {
        "id": "6c9c13e9"
      },
      "source": [
        "In this edition we will cover what we may call 'generic' agents which really able to perform many meta tasks. There are other more specific agents that are tuned for different tasks (called 'agent-toolkits'), but we will cover those in a future edition."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a93dc920",
      "metadata": {
        "id": "a93dc920"
      },
      "source": [
        "## Create database"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b601f903",
      "metadata": {
        "id": "b601f903"
      },
      "source": [
        "We will use the agents to interact with a small sample database of stocks. We will not dive into the details because this is just a dummy tool we will build for illustrative purposes. Let's create it."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "61b1f17c",
      "metadata": {
        "id": "61b1f17c"
      },
      "outputs": [],
      "source": [
        "from sqlalchemy import MetaData\n",
        "\n",
        "metadata_obj = MetaData()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "3cc1d80e",
      "metadata": {
        "id": "3cc1d80e"
      },
      "outputs": [],
      "source": [
        "from sqlalchemy import Column, Integer, String, Table, Date, Float\n",
        "\n",
        "stocks = Table(\n",
        "    \"stocks\",\n",
        "    metadata_obj,\n",
        "    Column(\"obs_id\", Integer, primary_key=True),\n",
        "    Column(\"stock_ticker\", String(4), nullable=False),\n",
        "    Column(\"price\", Float, nullable=False),\n",
        "    Column(\"date\", Date, nullable=False),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "c9a9571a",
      "metadata": {
        "id": "c9a9571a"
      },
      "outputs": [],
      "source": [
        "from sqlalchemy import create_engine\n",
        "\n",
        "engine = create_engine(\"sqlite:///:memory:\")\n",
        "metadata_obj.create_all(engine)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "81c3081f",
      "metadata": {
        "id": "81c3081f"
      },
      "outputs": [],
      "source": [
        "from datetime import datetime\n",
        "\n",
        "observations = [\n",
        "    [1, 'ABC', 200, datetime(2023, 1, 1)],\n",
        "    [2, 'ABC', 208, datetime(2023, 1, 2)],\n",
        "    [3, 'ABC', 232, datetime(2023, 1, 3)],\n",
        "    [4, 'ABC', 225, datetime(2023, 1, 4)],\n",
        "    [5, 'ABC', 226, datetime(2023, 1, 5)],\n",
        "    [6, 'XYZ', 810, datetime(2023, 1, 1)],\n",
        "    [7, 'XYZ', 803, datetime(2023, 1, 2)],\n",
        "    [8, 'XYZ', 798, datetime(2023, 1, 3)],\n",
        "    [9, 'XYZ', 795, datetime(2023, 1, 4)],\n",
        "    [10, 'XYZ', 791, datetime(2023, 1, 5)],\n",
        "]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "85fd20fa",
      "metadata": {
        "id": "85fd20fa"
      },
      "outputs": [],
      "source": [
        "from sqlalchemy import insert\n",
        "\n",
        "def insert_obs(obs):\n",
        "    stmt = insert(stocks).values(\n",
        "    obs_id=obs[0],\n",
        "    stock_ticker=obs[1],\n",
        "    price=obs[2],\n",
        "    date=obs[3]\n",
        "    )\n",
        "\n",
        "    with engine.begin() as conn:\n",
        "        conn.execute(stmt)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "6766f1f7",
      "metadata": {
        "id": "6766f1f7"
      },
      "outputs": [],
      "source": [
        "for obs in observations:\n",
        "    insert_obs(obs)"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "We are installing the `langchain_experimental` library here, since the `SQLDatabaseChain` is located there. This might be changed in the future and moved into official `langchain` library."
      ],
      "metadata": {
        "id": "H3_tIYndeNAt"
      },
      "id": "H3_tIYndeNAt"
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install langchain_experimental -qU"
      ],
      "metadata": {
        "id": "KJJz9xXvb1Ej",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "9533f802-804d-4c4c-c19f-87827bbc178c"
      },
      "id": "KJJz9xXvb1Ej",
      "execution_count": 12,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\u001b[?25l     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/70.7 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m70.7/70.7 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "9721648e",
      "metadata": {
        "id": "9721648e",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "9e77c155-c662-47c2-b585-deb88e8ad51b"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "/usr/local/lib/python3.10/dist-packages/langchain_experimental/sql/base.py:66: UserWarning: Directly instantiating an SQLDatabaseChain with an llm is deprecated. Please instantiate with llm_chain argument or using the from_llm class method.\n",
            "  warnings.warn(\n"
          ]
        }
      ],
      "source": [
        "from langchain.utilities import SQLDatabase\n",
        "from langchain_experimental.sql import SQLDatabaseChain\n",
        "\n",
        "db = SQLDatabase(engine)\n",
        "sql_chain = SQLDatabaseChain(llm=llm, database=db, verbose=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6325f758",
      "metadata": {
        "id": "6325f758"
      },
      "source": [
        "## Agent types"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4d732b7a",
      "metadata": {
        "id": "4d732b7a"
      },
      "source": [
        "In this section we will review several agents and see how they 'think' and what they can do."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8ee98631",
      "metadata": {
        "id": "8ee98631"
      },
      "source": [
        "Using one of langchain's pre-built agents involves three variables:\n",
        "* defining the tools or the toolkit\n",
        "* defining the llm\n",
        "* defining the agent type\n",
        "\n",
        "This is all really easy to do in langchain, as we will see in the following example."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1eaf306a",
      "metadata": {
        "id": "1eaf306a"
      },
      "source": [
        "### Agent type #1: Zero Shot React"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "In this first example we will use slightly different type of agent - SQL Agent which can be instantiated with it's own method `create_sql_agent`. Other agents will be instantiated in more generic way as we will see below in other examples.\n",
        "<br><br>\n",
        "This method uses *toolkit* instead of simple list of `tools`. You can read more about them in the [documentation](https://python.langchain.com/docs/modules/agents/toolkits/). For this use case, we will use `SQLDatabaseToolkit`."
      ],
      "metadata": {
        "id": "Tgn6dRLEcxli"
      },
      "id": "Tgn6dRLEcxli"
    },
    {
      "cell_type": "markdown",
      "source": [
        "As the name suggests, we will use this agent to perform 'zero shot' tasks on the input. That means that we will not have several, interdependent interactions but only one. In other words, this agent will have no memory."
      ],
      "metadata": {
        "id": "mie_37ERl_ac"
      },
      "id": "mie_37ERl_ac"
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now we are ready to initialize the agent! We will use `verbose` in `True` so we can see what is our agent's 'thinking' process."
      ],
      "metadata": {
        "id": "O_pYBlCcl11l"
      },
      "id": "O_pYBlCcl11l"
    },
    {
      "cell_type": "markdown",
      "source": [
        "**Important Note:** *When interacting with agents it is really important to set the `max_iterations` parameters because agents can get stuck in infinite loops that consume plenty of tokens. The default value is 15 to allow for many tools and complex reasoning but for most applications you should keep it much lower.*"
      ],
      "metadata": {
        "id": "GtSMUCaIlupp"
      },
      "id": "GtSMUCaIlupp"
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain.agents import create_sql_agent\n",
        "from langchain.agents.agent_toolkits import SQLDatabaseToolkit\n",
        "from langchain.agents.agent_types import AgentType\n",
        "\n",
        "agent_executor = create_sql_agent(\n",
        "    llm=llm,\n",
        "    toolkit=SQLDatabaseToolkit(db=db, llm=llm),\n",
        "    verbose=True,\n",
        "    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,\n",
        "    max_iterations=3\n",
        ")"
      ],
      "metadata": {
        "id": "5Z4EmMmqiOvZ"
      },
      "id": "5Z4EmMmqiOvZ",
      "execution_count": 14,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Let's see our newly created agent in action! We will ask it a question that involves a math operation over the stock prices."
      ],
      "metadata": {
        "id": "iBqz6aFzj-2d"
      },
      "id": "iBqz6aFzj-2d"
    },
    {
      "cell_type": "code",
      "source": [
        "result = count_tokens(\n",
        "    agent_executor,\n",
        "    \"What is the multiplication of the ratio between stock \" +\n",
        "    \"prices for 'ABC' and 'XYZ' in January 3rd and the ratio \" +\n",
        "    \"between the same stock prices in January the 4th?\"\n",
        ")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "MdvgpwHRic3W",
        "outputId": "5e63199b-2d92-404a-c712-5e8f4f55e23d"
      },
      "id": "MdvgpwHRic3W",
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
            "\u001b[32;1m\u001b[1;3mAction: sql_db_list_tables\n",
            "Action Input: \u001b[0m\n",
            "Observation: \u001b[38;5;200m\u001b[1;3mstocks\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m I should query the schema of the stocks table to see what columns I can use.\n",
            "Action: sql_db_schema\n",
            "Action Input: stocks\u001b[0m\n",
            "Observation: \u001b[33;1m\u001b[1;3m\n",
            "CREATE TABLE stocks (\n",
            "\tobs_id INTEGER NOT NULL, \n",
            "\tstock_ticker VARCHAR(4) NOT NULL, \n",
            "\tprice FLOAT NOT NULL, \n",
            "\tdate DATE NOT NULL, \n",
            "\tPRIMARY KEY (obs_id)\n",
            ")\n",
            "\n",
            "/*\n",
            "3 rows from stocks table:\n",
            "obs_id\tstock_ticker\tprice\tdate\n",
            "1\tABC\t200.0\t2023-01-01\n",
            "2\tABC\t208.0\t2023-01-02\n",
            "3\tABC\t232.0\t2023-01-03\n",
            "*/\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m I should query the ratio between the stock prices for 'ABC' and 'XYZ' in January 3rd and the ratio between the same stock prices in January the 4th.\n",
            "Action: sql_db_query\n",
            "Action Input: SELECT (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-03') / (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-03') / (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-04') * (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-04') AS ratio;\u001b[0m\n",
            "Observation: \u001b[36;1m\u001b[1;3m[(1.027234753550543,)]\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n",
            "Spent a total of 2398 tokens\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "As always, let's see what the prompt is here:"
      ],
      "metadata": {
        "id": "LqzuNMgWmFXS"
      },
      "id": "LqzuNMgWmFXS"
    },
    {
      "cell_type": "code",
      "source": [
        "print(agent_executor.agent.llm_chain.prompt.template)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ny7m_wMCmF4b",
        "outputId": "ab8fdf8a-6f83-4f3f-e9dc-7313b48f81ef"
      },
      "id": "ny7m_wMCmF4b",
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "You are an agent designed to interact with a SQL database.\n",
            "Given an input question, create a syntactically correct sqlite query to run, then look at the results of the query and return the answer.\n",
            "Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 10 results.\n",
            "You can order the results by a relevant column to return the most interesting examples in the database.\n",
            "Never query for all the columns from a specific table, only ask for the relevant columns given the question.\n",
            "You have access to tools for interacting with the database.\n",
            "Only use the below tools. Only use the information returned by the below tools to construct your final answer.\n",
            "You MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.\n",
            "\n",
            "DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.\n",
            "\n",
            "If the question does not seem related to the database, just return \"I don't know\" as the answer.\n",
            "\n",
            "\n",
            "sql_db_query: Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', using sql_db_schema to query the correct table fields.\n",
            "sql_db_schema: Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: 'table1, table2, table3'\n",
            "sql_db_list_tables: Input is an empty string, output is a comma separated list of tables in the database.\n",
            "sql_db_query_checker: Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!\n",
            "\n",
            "Use the following format:\n",
            "\n",
            "Question: the input question you must answer\n",
            "Thought: you should always think about what to do\n",
            "Action: the action to take, should be one of [sql_db_query, sql_db_schema, sql_db_list_tables, sql_db_query_checker]\n",
            "Action Input: the input to the action\n",
            "Observation: the result of the action\n",
            "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
            "Thought: I now know the final answer\n",
            "Final Answer: the final answer to the original input question\n",
            "\n",
            "Begin!\n",
            "\n",
            "Question: {input}\n",
            "Thought: I should look at the tables in the database to see what I can query.  Then I should query the schema of the most relevant tables.\n",
            "{agent_scratchpad}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5b2b47ed",
      "metadata": {
        "id": "5b2b47ed"
      },
      "source": [
        "The question we must ask ourselves here is: how are agents different than chains?"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f73aaf07",
      "metadata": {
        "id": "f73aaf07"
      },
      "source": [
        "If we look at the agent's logic and the prompt we have just printed we will see some clear differences. First, we have the tools which are included in the prompt. Second we have a thought process which was before was immediate in chains but now involves a 'thought', 'action', 'action input', 'observation' sequence. What is this all about?"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3a698f78",
      "metadata": {
        "id": "3a698f78"
      },
      "source": [
        "Suffice it to say for now that **the LLM now has the ability to 'reason' on how to best use tools** to solve our query and can combine them in intelligent ways with just a brief description of each of them. If you want to learn more about this paradigm (MRKL) in detail, please refer to [this](https://arxiv.org/pdf/2205.00445.pdf) paper."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aff9e65d",
      "metadata": {
        "id": "aff9e65d"
      },
      "source": [
        "Finally, let's pay attention to the 'agent_scratchpad'. What is that? Well, that is where we will be appending every thought or action that the agent has already performed. In this way, at each point in time, the agent will know what it has found out and will be able to continue its thought process. In other words, after using a tool it adds its thoughts and observations to the scratchpad and picks up from there."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "04d70642",
      "metadata": {
        "id": "04d70642"
      },
      "source": [
        "### Agent type #2: Conversational React"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ec589b1",
      "metadata": {
        "id": "1ec589b1"
      },
      "source": [
        "The zero shot agent is really interesting but, as we said before, it has no memory. What if we want an assistant that remembers things we have talked about and can also reason about them and use tools? For that we have the conversational react agent."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5c26c50f",
      "metadata": {
        "id": "5c26c50f"
      },
      "source": [
        "We will use the math tool in this example and load it as below:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "4b6faff3",
      "metadata": {
        "id": "4b6faff3"
      },
      "outputs": [],
      "source": [
        "from langchain.agents import load_tools\n",
        "\n",
        "tools = load_tools(\n",
        "    [\"llm-math\"],\n",
        "    llm=llm\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be45b903",
      "metadata": {
        "id": "be45b903"
      },
      "source": [
        "The memory type being used here is a simple buffer memory to allow us to remember previous steps in the reasoning chain. For more information on memory, please refer to the 3rd chapter of this series."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "0aff4edf",
      "metadata": {
        "id": "0aff4edf"
      },
      "outputs": [],
      "source": [
        "from langchain.memory import ConversationBufferMemory\n",
        "\n",
        "memory = ConversationBufferMemory(memory_key=\"chat_history\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "6579cef0",
      "metadata": {
        "id": "6579cef0"
      },
      "outputs": [],
      "source": [
        "from langchain.agents import initialize_agent\n",
        "\n",
        "conversational_agent = initialize_agent(\n",
        "    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,\n",
        "    tools=tools,\n",
        "    llm=llm,\n",
        "    verbose=True,\n",
        "    max_iterations=3,\n",
        "    memory=memory,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "cabbea50",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "cabbea50",
        "outputId": "760bde78-a553-46e0-da3b-433cd0196613"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
            "\u001b[32;1m\u001b[1;3m\n",
            "Thought: Do I need to use a tool? Yes\n",
            "Action: Calculator\n",
            "Action Input: 10,000, 8%, 5 years\u001b[0m\n",
            "Observation: \u001b[36;1m\u001b[1;3mAnswer: 14693.280768000006\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m Do I need to use a tool? No\n",
            "AI: The result of an investment of $10,000 growing at 8% annually for 5 years with compound interest is $14,693.28.\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n",
            "Spent a total of 1246 tokens\n"
          ]
        }
      ],
      "source": [
        "result = count_tokens(\n",
        "    conversational_agent,\n",
        "    \"What's the result of an investment of $10,000 growing at 8% annually for 5 years with compound interest?\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bdcd9847",
      "metadata": {
        "id": "bdcd9847"
      },
      "source": [
        "As we can see below, the prompt is similar but it includes a great prelude of instructions that make it an effective assistant as well + a spot for including the chat history from the memory component:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "de413fd3",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "de413fd3",
        "outputId": "f79dc083-db80-4dbb-8fb6-da5f6f603645"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Assistant is a large language model trained by OpenAI.\n",
            "\n",
            "Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.\n",
            "\n",
            "Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.\n",
            "\n",
            "Overall, Assistant is a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.\n",
            "\n",
            "TOOLS:\n",
            "------\n",
            "\n",
            "Assistant has access to the following tools:\n",
            "\n",
            "> Calculator: Useful for when you need to answer questions about math.\n",
            "\n",
            "To use a tool, please use the following format:\n",
            "\n",
            "```\n",
            "Thought: Do I need to use a tool? Yes\n",
            "Action: the action to take, should be one of [Calculator]\n",
            "Action Input: the input to the action\n",
            "Observation: the result of the action\n",
            "```\n",
            "\n",
            "When you have a response to say to the Human, or if you do not need to use a tool, you MUST use the format:\n",
            "\n",
            "```\n",
            "Thought: Do I need to use a tool? No\n",
            "AI: [your response here]\n",
            "```\n",
            "\n",
            "Begin!\n",
            "\n",
            "Previous conversation history:\n",
            "{chat_history}\n",
            "\n",
            "New input: {input}\n",
            "{agent_scratchpad}\n"
          ]
        }
      ],
      "source": [
        "print(conversational_agent.agent.llm_chain.prompt.template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32ee8041",
      "metadata": {
        "id": "32ee8041"
      },
      "source": [
        "Let's see what happens if we try to answer the question that is related to the previous one:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "5e109878",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "5e109878",
        "outputId": "51a80167-1e1b-4717-e3bc-bf25ad1c6cb7"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
            "\u001b[32;1m\u001b[1;3m\n",
            "Thought: Do I need to use a tool? Yes\n",
            "Action: Calculator\n",
            "Action Input: 15000, 8%, 5 years, compound interest\u001b[0m\n",
            "Observation: \u001b[36;1m\u001b[1;3mAnswer: 22039.92115200001\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m Do I need to use a tool? No\n",
            "AI: If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, we would have $22,039.92, which is $7,346.64 more than the previous scenario.\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n",
            "Spent a total of 1416 tokens\n"
          ]
        }
      ],
      "source": [
        "result = count_tokens(\n",
        "    conversational_agent,\n",
        "    \"If we start with $15,000 instead and follow the same 8% annual growth for 5 years with compound interest, how much more would we have compared to the previous scenario?\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "44135b8d",
      "metadata": {
        "id": "44135b8d"
      },
      "source": [
        "### Agent type #3: React Docstore"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e6d0d13",
      "metadata": {
        "id": "1e6d0d13"
      },
      "source": [
        "This type of agent is similar to the ones we have seen so far but it includes the interaction with a docstore. It will have two and only two tools at its disposal: 'Search' and 'Lookup'."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d569fd41",
      "metadata": {
        "id": "d569fd41"
      },
      "source": [
        "With 'Search' it will bring up a relevant article and with 'Lookup' the agent will find the right piece of information in the article. This is probably easiest to see in an example:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "ecc452af",
      "metadata": {
        "id": "ecc452af"
      },
      "outputs": [],
      "source": [
        "from langchain import Wikipedia\n",
        "from langchain.agents.react.base import DocstoreExplorer, Tool\n",
        "\n",
        "docstore=DocstoreExplorer(Wikipedia())\n",
        "tools = [\n",
        "    Tool(\n",
        "        name=\"Search\",\n",
        "        func=docstore.search,\n",
        "        description='search wikipedia'\n",
        "    ),\n",
        "    Tool(\n",
        "        name=\"Lookup\",\n",
        "        func=docstore.lookup,\n",
        "        description='lookup a term in wikipedia'\n",
        "    )\n",
        "]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "595938a1",
      "metadata": {
        "id": "595938a1"
      },
      "outputs": [],
      "source": [
        "docstore_agent = initialize_agent(\n",
        "    tools,\n",
        "    llm,\n",
        "    agent=\"react-docstore\",\n",
        "    verbose=True,\n",
        "    max_iterations=3\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "bba6b065",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "bba6b065",
        "outputId": "52d4f8f6-b56b-4894-9f18-dc126be884b4"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "\n",
            "\n",
            "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
            "\u001b[32;1m\u001b[1;3mThought: I need to search Archimedes and find his last words.\n",
            "Action: Search[Archimedes]\u001b[0m\n",
            "Observation: \u001b[36;1m\u001b[1;3mArchimedes of Syracuse (, ARK-ihm-EE-deez; c. 287 – c. 212 BC) was an Ancient Greek mathematician, physicist, engineer, astronomer, and inventor from the ancient city of Syracuse in Sicily. Although few details of his life are known, he is regarded as one of the leading scientists in classical antiquity. Considered the greatest mathematician of ancient history, and one of the greatest of all time, Archimedes anticipated modern calculus and analysis by applying the concept of the infinitely small and the method of exhaustion to derive and rigorously prove a range of geometrical theorems. These include the area of a circle, the surface area and volume of a sphere, the area of an ellipse, the area under a parabola, the volume of a segment of a paraboloid of revolution, the volume of a segment of a hyperboloid of revolution, and the area of a spiral.Archimedes' other mathematical achievements include deriving an approximation of pi, defining and investigating the Archimedean spiral, and devising a system using exponentiation for expressing very large numbers. He was also one of the first to apply mathematics to physical phenomena, working on statics and hydrostatics. Archimedes' achievements in this area include a proof of the law of the lever, the widespread use of the concept of center of gravity, and the enunciation of the law of buoyancy or Archimedes' principle. He is also credited with designing innovative machines, such as his screw pump, compound pulleys, and defensive war machines to protect his native Syracuse from invasion.\n",
            "Archimedes died during the siege of Syracuse, when he was killed by a Roman soldier despite orders that he should not be harmed. Cicero describes visiting Archimedes' tomb, which was surmounted by a sphere and a cylinder that Archimedes requested be placed there to represent his mathematical discoveries.\n",
            "Unlike his inventions, Archimedes' mathematical writings were little known in antiquity. Mathematicians from Alexandria read and quoted him, but the first comprehensive compilation was not made until c. 530 AD by Isidore of Miletus in Byzantine Constantinople, while commentaries on the works of Archimedes by Eutocius in the 6th century opened them to wider readership for the first time. The relatively few copies of Archimedes' written work that survived through the Middle Ages were an influential source of ideas for scientists during the Renaissance and again in the 17th century, while the discovery in 1906 of previously lost works by Archimedes in the Archimedes Palimpsest has provided new insights into how he obtained mathematical results.\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m The paragraph does not mention Archimedes' last words. I need to look up \"last words\".\n",
            "Action: Lookup[last words]\u001b[0m\n",
            "Observation: \u001b[33;1m\u001b[1;3m(Result 1/1) \n",
            "== Biography ==\n",
            "Archimedes was born c. 287 BC in the seaport city of Syracuse, Sicily, at that time a self-governing colony in Magna Graecia. The date of birth is based on a statement by the Byzantine Greek historian John Tzetzes that Archimedes lived for 75 years before his death in 212 BC. In the Sand-Reckoner, Archimedes gives his father's name as Phidias, an astronomer about whom nothing else is known. A biography of Archimedes was written by his friend Heracleides, but this work has been lost, leaving the details of his life obscure. It is unknown, for instance, whether he ever married or had children, or if he ever visited Alexandria, Egypt, during his youth. From his surviving written works, it is clear that he maintained collegiate relations with scholars based there, including his friend Conon of Samos and the head librarian Eratosthenes of Cyrene.The standard versions of Archimedes' life were written long after his death by Greek and Roman historians. The earliest reference to Archimedes occurs in The Histories by Polybius (c. 200–118 BC), written about 70 years after his death. It sheds little light on Archimedes as a person, and focuses on the war machines that he is said to have built in order to defend the city from the Romans. Polybius remarks how, during the Second Punic War, Syracuse switched allegiances from Rome to Carthage, resulting in a military campaign under the command of Marcus Claudius Marcellus and Appius Claudius Pulcher, who besieged the city from 213 to 212 BC. He notes that the Romans underestimated Syracuse's defenses, and mentions several machines Archimedes designed, including improved catapults, crane-like machines that could be swung around in an arc, and other stone-throwers. Although the Romans ultimately captured the city, they suffered considerable losses due to Archimedes' inventiveness.Cicero (106–43 BC) mentions Archimedes in some of his works. While serving as a quaestor in Sicily, Cicero found what was presumed to be Archimedes' tomb near the Agrigentine gate in Syracuse, in a neglected condition and overgrown with bushes. Cicero had the tomb cleaned up and was able to see the carving and read some of the verses that had been added as an inscription. The tomb carried a sculpture illustrating Archimedes' favorite mathematical proof, that the volume and surface area of the sphere are two-thirds that of an enclosing cylinder including its bases. He also mentions that Marcellus brought to Rome two planetariums Archimedes built. The Roman historian Livy (59 BC–17 AD) retells Polybius' story of the capture of Syracuse and Archimedes' role in it.\n",
            "Plutarch (45–119 AD) wrote in his Parallel Lives that Archimedes was related to King Hiero II, the ruler of Syracuse. He also provides at least two accounts on how Archimedes died after the city was taken. According to the most popular account, Archimedes was contemplating a mathematical diagram when the city was captured. A Roman soldier commanded him to come and meet Marcellus, but he declined, saying that he had to finish working on the problem. This enraged the soldier, who killed Archimedes with his sword. Another story has Archimedes carrying mathematical instruments before being killed because a soldier thought they were valuable items. Marcellus was reportedly angered by Archimedes' death, as he considered him a valuable scientific asset (he called Archimedes \"a geometrical Briareus\") and had ordered that he should not be harmed.The last words attributed to Archimedes are \"Do not disturb my circles\" (Latin, \"Noli turbare circulos meos\"; Katharevousa Greek, \"μὴ μου τοὺς κύκλους τάραττε\"), a reference to the mathematical drawing that he was supposedly studying when disturbed by the Roman soldier. There is no reliable evidence that Archimedes uttered these words and they do not appear in Plutarch's account. A similar quotation is found in the work of Valerius Maximus (fl. 30 AD), who wrote in Memorable Doings and Sayings, \"... sed protecto manibus puluere 'noli' inquit, 'obsecro, istum disturbare'\" (\"... but protecting the dust with his hands, said 'I beg of you, do not disturb this'\").\u001b[0m\n",
            "Thought:\u001b[32;1m\u001b[1;3m The paragraph mentions that the last words attributed to Archimedes are \"Do not disturb my circles\". So the answer is \"Do not disturb my circles\".\n",
            "Action: Finish[Do not disturb my circles]\u001b[0m\n",
            "\n",
            "\u001b[1m> Finished chain.\u001b[0m\n",
            "Spent a total of 6692 tokens\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'input': \"What were Archimedes' last words?\",\n",
              " 'output': 'Do not disturb my circles'}"
            ]
          },
          "metadata": {},
          "execution_count": 25
        }
      ],
      "source": [
        "count_tokens(docstore_agent, \"What were Archimedes' last words?\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a5f77ac",
      "metadata": {
        "id": "6a5f77ac"
      },
      "source": [
        "We will not print the prompt here because it is too large, but you can see it yourself if you want to (we know how to already).\n",
        "\n",
        "In short, it contains several examples of the `Question` > `Thought` > `Action` > `Observation` loop, that include the `Search` and `Lookup` tools.\n",
        "\n",
        "If you want to learn more about this approach [this](https://arxiv.org/pdf/2210.03629.pdf) is the paper for ReAct"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4aae3b5b",
      "metadata": {
        "id": "4aae3b5b"
      },
      "source": [
        "### Agent type #4: Self Ask with Search"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c07039e5",
      "metadata": {
        "id": "c07039e5"
      },
      "source": [
        "This is the first-choice agent to use when using LLM's to extract information with a search engine. The agent will ask follow-up questions and use the search functionality to get intermediate answers that help it get to a final answer."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "903660b2",
      "metadata": {
        "id": "903660b2"
      },
      "outputs": [],
      "source": [
        "from langchain import OpenAI, SerpAPIWrapper\n",
        "from langchain.agents import initialize_agent, Tool\n",
        "\n",
        "search = SerpAPIWrapper(serpapi_api_key='api_key')\n",
        "tools = [\n",
        "    Tool(\n",
        "        name=\"Intermediate Answer\",\n",
        "        func=search.run,\n",
        "        description='google search'\n",
        "    )\n",
        "]\n",
        "\n",
        "self_ask_with_search = initialize_agent(tools, llm, agent=\"self-ask-with-search\", verbose=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ec1c4c8",
      "metadata": {
        "id": "0ec1c4c8"
      },
      "source": [
        "We will not interact with this agent because for that we would need a serpapi key. However, by checking out the prompt we can see a few examples of how it works:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "e4134930",
      "metadata": {
        "id": "e4134930",
        "outputId": "2f58884d-0c4e-4b40-de45-6928e0c60ef4",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Question: Who lived longer, Muhammad Ali or Alan Turing?\n",
            "Are follow up questions needed here: Yes.\n",
            "Follow up: How old was Muhammad Ali when he died?\n",
            "Intermediate answer: Muhammad Ali was 74 years old when he died.\n",
            "Follow up: How old was Alan Turing when he died?\n",
            "Intermediate answer: Alan Turing was 41 years old when he died.\n",
            "So the final answer is: Muhammad Ali\n",
            "\n",
            "Question: When was the founder of craigslist born?\n",
            "Are follow up questions needed here: Yes.\n",
            "Follow up: Who was the founder of craigslist?\n",
            "Intermediate answer: Craigslist was founded by Craig Newmark.\n",
            "Follow up: When was Craig Newmark born?\n",
            "Intermediate answer: Craig Newmark was born on December 6, 1952.\n",
            "So the final answer is: December 6, 1952\n",
            "\n",
            "Question: Who was the maternal grandfather of George Washington?\n",
            "Are follow up questions needed here: Yes.\n",
            "Follow up: Who was the mother of George Washington?\n",
            "Intermediate answer: The mother of George Washington was Mary Ball Washington.\n",
            "Follow up: Who was the father of Mary Ball Washington?\n",
            "Intermediate answer: The father of Mary Ball Washington was Joseph Ball.\n",
            "So the final answer is: Joseph Ball\n",
            "\n",
            "Question: Are both the directors of Jaws and Casino Royale from the same country?\n",
            "Are follow up questions needed here: Yes.\n",
            "Follow up: Who is the director of Jaws?\n",
            "Intermediate answer: The director of Jaws is Steven Spielberg.\n",
            "Follow up: Where is Steven Spielberg from?\n",
            "Intermediate answer: The United States.\n",
            "Follow up: Who is the director of Casino Royale?\n",
            "Intermediate answer: The director of Casino Royale is Martin Campbell.\n",
            "Follow up: Where is Martin Campbell from?\n",
            "Intermediate answer: New Zealand.\n",
            "So the final answer is: No\n",
            "\n",
            "Question: {input}\n",
            "Are followup questions needed here:{agent_scratchpad}\n"
          ]
        }
      ],
      "source": [
        "print(self_ask_with_search.agent.llm_chain.prompt.template)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3e7790ae",
      "metadata": {
        "id": "3e7790ae"
      },
      "source": [
        "As we can see, the prompt is basically a series of many examples to show the LLM how to ask follow up questions to a search tool until it can get to the final answer."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5df324ba",
      "metadata": {
        "id": "5df324ba"
      },
      "source": [
        "And.. again [here](https://arxiv.org/pdf/2210.03350.pdf) you have the paper to dive deeper!"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4cd80a08",
      "metadata": {
        "id": "4cd80a08"
      },
      "source": [
        "### Wrapping up"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "97d108e2",
      "metadata": {
        "id": "97d108e2"
      },
      "source": [
        "And that all for agents! There's many other things you can do with agents, just to name a few:\n",
        "* Create your own custom agent\n",
        "* Use them with many other tools (even custom ones)\n",
        "* Trace every call an agent makes through a convinient UI interface"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e33dd90a",
      "metadata": {
        "id": "e33dd90a"
      },
      "source": [
        "Check the how-to [guides](https://langchain.readthedocs.io/en/latest/modules/agents/how_to_guides.html) for more!"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "name": "python"
    },
    "vscode": {
      "interpreter": {
        "hash": "578e1e8dce4dc6c542f1ea2d66a2d9db6ef592936dcc314004bdae386f827d38"
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}